package org.needcoke.hs.signature;

import cn.hutool.core.util.StrUtil;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.needcoke.hs.bean.InterceptRule;
import org.needcoke.hs.http.Request;
import org.needcoke.hs.util.SignatureUtil;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Slf4j
@ToString
public abstract class AbstractSignature implements Signature {


    protected final Map<String, String> params = new HashMap<>();

    protected String body = "";

    protected String proof = "";  // 1

    protected boolean proofEnd = true;   //1

    @Getter
    protected String connectorBetweenNameAndValue = "=";   // 1

    @Getter
    protected String connectorBetweenTwoGroup = "&";      // 1

    protected SignFunction signFunction = SignFunction.defaultSignFunction;      //1

    protected SortedFunction sortedFunction = SortedFunction.defaultSortedFunction;   // 1

    protected SignCode signCode = SignCode.HEX;          // 1

    protected InterceptRule interceptRule;               //  2

    protected SignatureLocation signatureLocation = SignatureLocation.HTTP_HEADER;      // 1

    protected String signatureName = "signature";          // 1

    protected boolean withFormBody = true;                 // 1

    protected boolean withApplicationBody = true;          // 1

    @Getter
    protected String connectorBetweenBodyAndParam = "";     // 1

    protected boolean bodyBefore = false;                   // 1

    @Getter
    protected String prefix = "";                           // 1
    @Getter
    protected String suffix = "";                           // 1
    @Getter
    protected String around = "";                           // 1

    protected String signKey = "";                          // 1


    @Override
    public void addParam(String name, String value) {
        params.put(name, value);
    }

    @Override
    public void addParam(Map<String, String> paramMap) {
        if (null != paramMap) {
            params.putAll(paramMap);
        }
    }

    @Override
    public void addApplicationBody(String body) {
        this.body = body;
    }

    @Override
    public void setConnectorBetweenNameAndValue(String connector) {
        if (StrUtil.isNotEmpty(connector)) {
            this.connectorBetweenNameAndValue = connector;
        }
    }

    @Override
    public void setConnectorBetweenTwoGroup(String connector) {
        if (StrUtil.isNotEmpty(connector)) {
            this.connectorBetweenTwoGroup = connector;
        }
    }

    @Override
    public void setProof(String proof, boolean end) {
        if (StrUtil.isNotEmpty(proof)) {
            this.proof = proof;
        }

    }

    @Override
    public void setProof(String proof) {
        if (StrUtil.isNotEmpty(proof)) {
            this.proof = proof;
        }
    }

    @Override
    public void setSortedFunction(SortedFunction function) {
        this.sortedFunction = function;
    }

    public abstract void setSortedFunction(String functionName);

    @Override
    public void setSignFunction(SignFunction function) {
        this.signFunction = function;
    }

    public abstract void setSignFunction(String functionName);

    @Override
    public void setSignCode(SignCode signCode) {
        this.signCode = signCode;
    }

    public abstract void setSignCode(String signCode);

    public String getSortedParamStr() {
        Set<String> keySet = params.keySet();
        List<String> sortedList = sortedFunction.sort(keySet);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < sortedList.size(); i++) {
            sb.append(sortedList.get(i));
            sb.append(connectorBetweenNameAndValue);
            sb.append(params.get(sortedList.get(i)));
            if (i < sortedList.size() - 1) {
                sb.append(connectorBetweenTwoGroup);
            }
        }
        return sb.toString();
    }

    /**
     * 编码
     */
    public String code(String signText) {
        if (signCode == SignCode.HEX) {
            return SignatureUtil.hex(signText);
        } else if (signCode == SignCode.BASE64) {
            return SignatureUtil.base64(signText);
        } else if (signCode == SignCode.NO) {
            return signText;
        }
        throw new RuntimeException("不支持的编码格式");
    }

    @Override
    public abstract Signature copy();

    @Override
    public boolean isWithFormBody() {
        return withFormBody;
    }

    @Override
    public boolean isWithApplicationBody() {
        return withApplicationBody;
    }

    @Override
    public String sign() {
        //String plaintext = getSortedParamStr() + (withFormBody && StrUtil.isNotEmpty(body) ? body : "");
        String plaintext = "";
        if (StrUtil.isNotEmpty(body)) {
            if (bodyBefore) {
                plaintext = body + connectorBetweenBodyAndParam + getSortedParamStr();
            } else {
                plaintext = getSortedParamStr() + connectorBetweenBodyAndParam + body;
            }
        } else {
            plaintext = getSortedParamStr();
        }
        plaintext = around + prefix + plaintext + suffix + around;
        String signText = signFunction.sign(plaintext, signKey);
        if (proofEnd) {
            signText = signText + proof;
        } else {
            signText = proof + signText;
        }
        String code = code(signText);
        log.info("url pattern --- {} , plaintext --- {} , signText --- {} , signature --- {} , signature config = {}!", interceptRule, plaintext, signText, code, this);
        return code;
    }

    @Override
    public void setInterceptRule(InterceptRule interceptRule) {
        if (null != interceptRule) {
            this.interceptRule = interceptRule;
        }
    }

    @Override
    public void setSignatureLocation(SignatureLocation location) {
        this.signatureLocation = location;
    }

    @Override
    public void setSignatureName(String name) {
        if (StrUtil.isNotEmpty(name)) {
            this.signatureName = name;
        }
    }

    @Override
    public void preventSignature(Request request, String signature) {
        if (signatureLocation == SignatureLocation.HTTP_HEADER) {
            request.addHeader(signatureName, signature);
            return;
        }
        if (signatureLocation == SignatureLocation.HTTP_URL_PARAM) {
            request.addUrlParameter(signatureName, signature);
        }
    }

    public void preventSignature(Request request) {
        String signature = sign();
        preventSignature(request, signature);
    }

    @Override
    public void setWithFormBody(boolean withFormBody) {
        this.withFormBody = withFormBody;
    }


    @Override
    public void setWithApplicationBody(boolean withApplicationBody) {
        this.withApplicationBody = withApplicationBody;
    }

    @Override
    public void setConnectorBetweenBodyAndParam(String connectorBetweenBodyAndParam) {
        if (StrUtil.isNotEmpty(connectorBetweenBodyAndParam)) {
            this.connectorBetweenBodyAndParam = connectorBetweenBodyAndParam;
        }
    }

    @Override
    public void setBodyBefore(boolean bodyBefore) {
        this.bodyBefore = bodyBefore;
    }

    @Override
    public void setPrefix(String prefix) {
        if (StrUtil.isNotEmpty(prefix)) {
            this.prefix = prefix;
        }
    }

    @Override
    public void setSuffix(String suffix) {
        if (StrUtil.isNotEmpty(suffix)) {
            this.suffix = suffix;
        }
    }

    @Override
    public void setAround(String around) {
        if (StrUtil.isNotEmpty(around)) {
            this.around = around;
        }
    }

    @Override
    public void setSignKey(String signKey) {
        if (StrUtil.isNotEmpty(signKey)) {
            this.signKey = signKey;
        }
    }
}
